/*
 * Fmp4MediaMuxer.cpp
 *
 *  Created on: 2021-08-02
 *      Author: chuanjiang.zh
 */

#include "Fmp4MediaMuxer.h"
#include "fmp4-writer.h"

#include "MovMemoryBuffer.h"
#include "Mp4Muxer.h"

#define AV_TIME_BASE            1000000


class Fmp4MediaMuxerPrivate
{
public:

	Fmp4MediaMuxerPrivate()
	{
	}

	void firePacket(UMediaPacket& pkt)
	{
		if (m_sink)
		{
			m_sink(pkt);
		}
	}

	void setSink(UMediaMuxer::Sink sink)
	{
		m_sink = sink;
	}

	Mp4Muxer	m_muxer;
	UMediaMuxer::Sink	m_sink;

};



Fmp4MediaMuxer::Fmp4MediaMuxer() :
	m_pimp(new Fmp4MediaMuxerPrivate())
{
}

Fmp4MediaMuxer::~Fmp4MediaMuxer()
{
	close();

	delete m_pimp;
	m_pimp = nullptr;
}


bool Fmp4MediaMuxer::open(const UMediaFormat& fmt)
{
	m_pimp->m_muxer.open(fmt);
	m_format = fmt;

	auto sink = std::bind(&Fmp4MediaMuxer::handlePacket, this, std::placeholders::_1,
		std::placeholders::_2, std::placeholders::_3, std::placeholders::_4);
	m_pimp->m_muxer.setSink(sink);

	return m_format.isValid();
}

void Fmp4MediaMuxer::close()
{
	m_pimp->m_muxer.close();
}

bool Fmp4MediaMuxer::isOpen()
{
	return m_format.isValid();
}


bool Fmp4MediaMuxer::getOutFormat(UMediaFormat& fmt)
{
	fmt = m_format;
	return m_format.isValid();
}

bool Fmp4MediaMuxer::input(UMediaPacket& pkt)
{
	if (pkt.size() <= 0)
	{
		return false;
	}

	if (!m_pimp)
	{
		return false;
	}

	int64_t pts = pkt.pts();
	int64_t dts = pkt.dts();

	if (pkt.isVideo())
	{
		if (m_format.m_clockRate == AV_TIME_BASE || m_format.m_clockRate == 0)
		{
			pts = pkt.pts() / 1000;
			dts = pkt.dts() / 1000;
		}
		else
		{
			pts = pkt.pts() * 1000 / m_format.m_clockRate;
			dts = pkt.dts() * 1000 / m_format.m_clockRate;
		}
	}
	else if (pkt.isAudio())
	{
		if (m_format.m_audioRate == AV_TIME_BASE || m_format.m_audioRate == 0)
		{
			pts = pkt.pts() / 1000;
			dts = pkt.dts() / 1000;
		}
		else
		{
			pts = pkt.pts() * 1000 / m_format.m_audioRate;
			dts = pkt.dts() * 1000 / m_format.m_audioRate;
		}
	}

	return m_pimp->m_muxer.write(pkt.type(), pkt.data(), pkt.size(), pts, dts, pkt.flags());
}

void Fmp4MediaMuxer::setSink(Sink sink)
{
	m_pimp->setSink(sink);
}

void Fmp4MediaMuxer::handlePacket(uint8_t* data, int size, int64_t pts, int flags)
{
	UMediaPacket packet;
	packet.newPacket(size);
	packet.set_type(UMEDIA_TYPE_DATA);
	packet.set_pts(pts);
	packet.set_dts(pts);
	packet.set_flags(flags);

	packet.copy(data, size);

	m_pimp->firePacket(packet);
}

const char* Fmp4MediaMuxer::getName()
{
	return "fmp4";
}

void Fmp4MediaMuxer::flush()
{
	m_pimp->m_muxer.flush();
}